﻿/*
 * Car Main Window
 */
#define _test
#define _new
//#define _simulation
#define _gpsspeed
#define _lastshot

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using EgeUniversity.SolarCarProject;
using System.IO;
using System.IO.Ports;
using System.Diagnostics;

namespace Car
{
	/// <summary>
	/// Interaction logic for MainWindow.xaml
	/// </summary>
	public partial class MainWindow : Window
	{
		//--------------------------------------------------------------------------------------------------------------
		#region Delegates
		//Cross Thread operations
		delegate void ChangeColorDelegate(Brush b);
		delegate void ChangeTextDelegate(TextBlock t, string s);
		delegate void ChangeVelocityDelegate(double d);
		delegate void ChangeGPSDelegate(Point p);
		delegate void ChangeCarValueDelegate(string s);
		#endregion
		//--------------------------------------------------------------------------------------------------------------
		#region Global Properties
		public SolarCar Car { get; set; }
		public ClientClass Client { get; set; }	//TCP/IP Client
		#endregion
		//--------------------------------------------------------------------------------------------------------------
#if _simulation
		List<Point> SimGPS = new List<Point>();
		List<double> SimVelocity = new List<double>();
		List<double> SimSOC = new List<double>();
		List<double> SimBatTemp = new List<double>();
		List<double> SimPVTemp = new List<double>();
		List<double> SimBatV = new List<double>();
		List<double> SimBatCur = new List<double>();
		List<double> SimPVV = new List<double>();
		List<double> SimPVC = new List<double>();
		List<double> SimPVTemp2 = new List<double>();
		List<double> SimPVTemp3 = new List<double>();
		List<double> SimPVV2 = new List<double>();
		List<double> SimPVC2 = new List<double>();
		int lap = 0;
#endif
		#region Local Variables
		DispatcherTimer timer;
		SerialPort		serial;
		string			data;
		#endregion
		//--------------------------------------------------------------------------------------------------------------
		public MainWindow()
		{
			InitializeComponent();
#if _test
			//If not test, full screen
			this.WindowState = WindowState.Normal;
#endif
		}
		//--------------------------------------------------------------------------------------------------------------
		private void Window_Loaded(object sender, RoutedEventArgs e)
		{
#if _simulation
			using (StreamReader reader = new StreamReader("sim.txt"))
			{
				Random rnd = new Random();
			    while (!reader.EndOfStream)
			    {
			        string[] str = reader.ReadLine().Split('#');
			        SimGPS.Add(new Point(Double.Parse(str[0]), Double.Parse(str[1])));
			        SimVelocity.Add(Double.Parse(str[2]));
			        SimSOC.Add(Double.Parse(str[3]));
			        SimBatTemp.Add(Double.Parse(str[4]));
			        SimPVTemp.Add(Double.Parse(str[5]));
			        SimBatCur.Add(Double.Parse(str[6]));
			        SimPVV.Add(Double.Parse(str[7]));
			        SimPVC.Add(Double.Parse(str[8]));
			        SimBatV.Add(Double.Parse(str[9]));
					SimPVV2.Add(Double.Parse(str[7])+rnd.NextDouble());
					SimPVC2.Add(Double.Parse(str[8])+rnd.NextDouble());
					SimPVTemp2.Add(Double.Parse(str[5])+rnd.NextDouble());
					SimPVTemp3.Add(Double.Parse(str[5])+rnd.NextDouble());
			    }
			}
#endif
			//Create objects
			Car = new SolarCar();
			Client = new ClientClass();
			//Client data changed event handler
			Client.ConnectionChanged += new StatusChangedEventHandler(Client_ConnectionChanged);
			Client.Start();	//start listening

			//Car data - UI binding
			Binding binding = new Binding();
			binding.Source = Car;
#if _gpsspeed
			binding.Path = new PropertyPath("Velocity");
#else
			binding.Path = new PropertyPath("Velocity2");
#endif
			binding.Mode = BindingMode.OneWay;	//When car values change, update visuals.
			binding.StringFormat = "0";
			textSpeed.SetBinding(TextBlock.TextProperty, binding);

			binding = new Binding();
			binding.Source = Car;
			binding.Path = new PropertyPath("BatteryStateOfCharge");
			binding.Mode = BindingMode.OneWay;
			progressBatterySOC.SetBinding(ProgressBar.ValueProperty, binding);

			//Serial communication
			serial = new SerialPort();
			serial.BaudRate = 9600;
			serial.PortName = "COM1";
			serial.DataReceived += new SerialDataReceivedEventHandler(serial_DataReceived);
			try { serial.Open(); }
			catch { }

			//Sending data timer
			timer = new DispatcherTimer();
#if _simulation
			timer.Interval = TimeSpan.FromMilliseconds(20);
#endif
			timer.Interval = TimeSpan.FromMilliseconds(200);
			timer.Tick += new EventHandler(timer_Tick);
			timer.Start();
		}
		//--------------------------------------------------------------------------------------------------------------
		int startIndex, lastIndex;
		void serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
		{//Serial Port data received
			try
			{
				data += serial.ReadExisting();	//Read incoming data
				//NMEA parsing (GPS data)
				startIndex = data.LastIndexOf("$");
				lastIndex = data.IndexOf("\n", startIndex);

				if (startIndex >= 0 && lastIndex >= 0)
				{
					Car.Dispatcher.Invoke(
						new ChangeCarValueDelegate(ChangeCarValue),
						data.Substring(startIndex + 1, lastIndex - startIndex));
					data = String.Empty;
				}
			}
			catch
			{
				//If there is a problem
				MessageBox.Show("serial : "+data);
			}
		}
		//--------------------------------------------------------------------------------------------------------------
		void Client_ConnectionChanged(ConnectionStatus e)
		{
			//Network connection status changed?
			if (e == ConnectionStatus.Connected)
				myConnectionStatus.Dispatcher.Invoke(
					new ChangeColorDelegate(ChangeColor),
					Brushes.Green);
			else
				myConnectionStatus.Dispatcher.Invoke(
					new ChangeColorDelegate(ChangeColor),
					Brushes.Red);
		}
		//--------------------------------------------------------------------------------------------------------------

		#region UI Delegates
		void ChangeColor(Brush b)
		{
			myConnectionStatus.Fill = b;
		}

		void ChangeText(TextBlock t, string s)
		{
			t.Text = s;
		}

		void ChangeVelocity(double d)
		{
			Car.Velocity = (int)d;
			rotateVelocity.Angle = Calculations.Map(Car.Velocity, 0, 100, -55, 55);
		}

		void ChangeGPS(Point p)
		{
			Car.GPSLocation = p;
		}

		void ChangeCarValue(string str)
		{
			textInfo.Dispatcher.Invoke(
				new ChangeTextDelegate(ChangeText),
				textInfo,
				str);
			Car.SetCarValue(str);
		}
		#endregion

		//--------------------------------------------------------------------------------------------------------------
#if _simulation
		int simindex = 0;
#endif
		void timer_Tick(object sender, EventArgs e)
		{
			timer.Stop();
#if _simulation
			if (Keyboard.IsKeyDown(Key.Up))
			{
			    Car.Velocity++;
			}
			else if (Keyboard.IsKeyDown(Key.Down))
			{
			    Car.Velocity--;
			}
			else if (Keyboard.IsKeyDown(Key.Escape))
			    App.Current.Shutdown();

			Car.GPSLocation = SimGPS[simindex];
			Car.Velocity = SimVelocity[simindex];
			Car.BatteryStateOfCharge = SimSOC[simindex];
			Car.BatteryTemperature = SimBatTemp[simindex];
			Car.PVTemperature1 = SimPVTemp[simindex];
			Car.BatteryCurrent = SimBatCur[simindex];
			Car.PVVoltage1 = SimPVV[simindex];
			Car.PVCurrent1 = SimPVC[simindex];
			Car.BatteryVoltage = SimBatV[simindex];
			rotateVelocity.Angle = Calculations.Map(Car.Velocity, 0, 100, -55, 55);
			progressBatterySOC.Value = Car.BatteryStateOfCharge;

			Car.PVVoltage2 = SimPVV2[simindex];
			Car.PVCurrent2 = SimPVC2[simindex];
			Car.PVTemperature2 = SimPVTemp2[simindex];
			Car.PVTemperature3 = SimPVTemp3[simindex];
			
			simindex++;
			if (simindex >= SimGPS.Count)
			{
			    simindex = 0;
			    lap++;
			    if (lap > 50) lap = 0;
			}
#endif
			//Send data with seperator
			Client.Send(String.Format("${0}#{1}#{2}#{3}#{4}\n",
				Car.BatteryVoltage,
				Car.BatteryCurrent,
				Car.MotorCurrent,
				Car.MotorTemperature,
				Car.Velocity));

			if (!serial.IsOpen)
			{
				try
				{
					serial.Open();
				}
				catch{}
			}
			//Client.Send(String.Format("{0}#{1}#{2}#{3}#{4}#{5}#{6}#{7}#{8}#{9}",
			//                    SimGPS[simindex].X.ToString(),
			//                    SimGPS[simindex].Y.ToString(),
			//                    SimVelocity[simindex],
			//                    SimSOC[simindex],
			//                    SimBatTemp[simindex],
			//                    SimPVTemp[simindex],
			//                    SimBatCur[simindex],
			//                    SimPVV[simindex],
			//                    SimPVC[simindex],
			//                    SimBatV[simindex]));
//            if (String.Format("${0}#{1}#{2}#{3}#{4}#{5}#{6}#{7}#{8}#{9}#{10}#{11}#{12}#{13}#{14}#{15}\n",
//                    Car.GPSLocation.X,
//                    Car.GPSLocation.Y,
//                    Car.Altitude,
//                    Car.Velocity,
//                    Car.Velocity2,
//                    Car.BatteryStateOfCharge,
//                    Car.BatteryCurrent,
//                    Car.BatteryVoltage,
//                    Car.BatteryTemperature,
//                    Car.PVVoltage1,
//                    Car.PVVoltage2,
//                    Car.PVCurrent1,
//                    Car.PVCurrent2,
//                    Car.PVTemperature1,
//                    Car.PVTemperature2,
//                    Car.PVTemperature3
//                    ).Split('#').Length < 16)
//            {
//                MessageBox.Show("<16");
//                System.Diagnostics.Debugger.Break();
//            }

//#if _new
//            Client.Send(String.Format("${0}#{1}#{2}#{3}#{4}#{5}#{6}#{7}#{8}#{9}#{10}#{11}#{12}#{13}#{14}#{15}#{16}#{17}\n",
//                    Car.GPSLocation.X/100,//0
//                    Car.GPSLocation.Y/100,//1
//                    Car.Altitude,//2
//                    Car.Velocity,//3
//                    Car.Velocity2,//4
//                    Car.BatteryVoltage,//5
//                    Car.BatteryCurrent,//6
//                    Car.PVVoltage1,//7
//                    Car.PVCurrent1,//8
//                    Car.PVVoltage2,//9
//                    Car.PVCurrent2,//10
//                    65,//11 motor voltage
//                    20,//12 motor current
//                    Car.PVTemperature1,//13
//                    Car.PVTemperature2,//14
//                    Car.PVTemperature3,//15
//                    20,//16 temp4
//                    Car.BatteryStateOfCharge//17
//                    ));
//#else
//            Client.Send(String.Format("${0}#{1}#{2}#{3}#{4}#{5}#{6}#{7}#{8}#{9}#{10}#{11}#{12}#{13}#{14}#{15}\n",
//                    Car.GPSLocation.X/100,//0
//                    Car.GPSLocation.Y/100,//1
//                    Car.Altitude,//2
//                    Car.Velocity,//3
//                    Car.Velocity2,//4
//                    Car.BatteryStateOfCharge,//5
//                    Car.BatteryCurrent,//6
//                    Car.BatteryVoltage,//7
//                    Car.BatteryTemperature,//8
//                    Car.PVVoltage1,//9
//                    Car.PVVoltage2,//10
//                    Car.PVCurrent1,//11
//                    Car.PVCurrent2,//12
//                    Car.PVTemperature1,//13
//                    Car.PVTemperature2,//14
//                    Car.PVTemperature3//15
//                    ));
//#endif
			timer.Start();
		}
		//--------------------------------------------------------------------------------------------------------------
		private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			//While developing, window is movable
			//TODO: use preprocessor
			DragMove();
		}

		private void Window_KeyDown(object sender, KeyEventArgs e)
		{
			//When developing
			//Car doesn't have any keyboard
			//TODO: use preprocessor
			if (e.Key == Key.Escape)
			{
				if (serial.IsOpen)
					serial.Close();
				App.Current.Shutdown();
				Process.GetCurrentProcess().Kill();
			}
			if (e.Key == Key.Enter)
				serial.Write("$PMTK101*32\r\n");
			if (e.Key == Key.W)
				Car.Velocity++;
			else if (e.Key == Key.S)
				Car.Velocity--;
		}

		private void Window_MouseDoubleClick(object sender, MouseButtonEventArgs e)
		{
			//When developing
			//TODO: use preprocessor
			Process.GetCurrentProcess().Kill();
			if (serial.IsOpen)
				serial.Close();
			App.Current.Shutdown();
		}
	}
}
